package com.yuxl.common.socket.dispatcher;

import com.yuxl.common.core.domain.auth.JwtUser;
import com.yuxl.common.core.domain.socket.Message;
import com.yuxl.common.socket.annotation.SocketMessageMapping;
import com.alibaba.fastjson2.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.socket.WebSocketSession;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.beans.factory.SmartInitializingSingleton;
import reactor.core.publisher.Mono;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.ParameterizedType;

@Component
public class MessageDispatcher implements SmartInitializingSingleton {
    @Autowired
    private ApplicationContext applicationContext;
    private final Map<String, Method> messageHandlerMap = new HashMap<>();
    private final Map<String, Object> handlerBeanMap = new HashMap<>();

    @Override
    public void afterSingletonsInstantiated() {
        // 只在所有单例Bean初始化后扫描，避免循环依赖
        String basePackage = "com.yuxl";
        String[] beanNames = applicationContext.getBeanNamesForAnnotation(Component.class);
        for (String beanName : beanNames) {
            Object bean = applicationContext.getBean(beanName);
            if (bean.getClass().getPackageName().startsWith(basePackage)) {
                for (Method method : bean.getClass().getMethods()) {
                    SocketMessageMapping mapping = method.getAnnotation(SocketMessageMapping.class);
                    if (mapping != null) {
                        messageHandlerMap.put(mapping.value(), method);
                        handlerBeanMap.put(mapping.value(), bean);
                    }
                }
            }
        }
    }

    public Mono<WebSocketMessage> dispatch(JwtUser user, WebSocketSession session, WebSocketMessage message) {
        String payload = message.getPayloadAsText();
        String path = null;
        Object data = null;
        try {
            var jsonObj = JSON.parseObject(payload);
            path = jsonObj.getString("path");
            data = jsonObj.get("data");
        } catch (Exception e) {
            return Mono.just(session.textMessage("""
                                                {"path":"/500","data":"消息处理异常"}
                        """));
        }
        if (path != null && messageHandlerMap.containsKey(path)) {
            Method method = messageHandlerMap.get(path);
            Object bean = handlerBeanMap.get(path);
            try {
                Message<Object> msgObj = new Message<>();
                msgObj.setUser(user);
                msgObj.setSession(session);
                // 自动转换data为目标泛型类型
                Object dataObj = data;
                if (method.getParameterCount() == 1) {
                    var paramType = method.getGenericParameterTypes()[0];
                    if (paramType instanceof ParameterizedType pt) {
                        var actualType = pt.getActualTypeArguments()[0];
                        if (actualType instanceof Class<?> clazz && data != null && data instanceof com.alibaba.fastjson2.JSONObject) {
                            dataObj = ((com.alibaba.fastjson2.JSONObject) data).toJavaObject(clazz);
                        }
                    }
                }
                msgObj.setData(dataObj);
                Object result = method.invoke(bean, msgObj);
                if (result == null) {
                    return Mono.empty(); // 不返回任何消息
                }
                if (result instanceof String str) {
                    return Mono.just(session.textMessage(str));
                } else if (result instanceof Mono<?> mono) {
                    return mono.map(obj -> session.textMessage(obj.toString()));
                } else {
                    return Mono.just(session.textMessage(result.toString()));
                }
            } catch (Exception e) {
                return Mono.just(session.textMessage(String.format("""
                                                {"path":"/500","data":"消息处理异常:%s"}
                        """, e.getMessage())));
            }
        }
        return Mono.just(session.textMessage(String.format("""
                                                {"path":"/404","data":"未找到消息处理器:%s"}
                        """, path)));
    }
} 